// Group
import Promise from 'bluebird'
import _ from 'lodash'
import { groupDao as Dao } from '../models'
import { addAndUpdateKeys, updateLastKeys } from './seq'
import * as userProxy from './user'
import { isMaster } from '../middlewares/auth'
import { CODE, ErrorInfo } from '../error'
import { callback } from '../utils'

const create = info => {
  return new Promise((resolve, reject) => {
    Dao.create(info, (err, doc) => callback(resolve, reject, err, doc))
  })
}

const findOne = (query, populate = null, fields = null) => {
  return new Promise((resolve, reject) => {
    Dao.model.findOne(query)
      .populate(populate || { path: '' })
      .select(fields)
      .exec((err, doc) => callback(resolve, reject, err, doc))
  })
}

const find = (query, populate = null, fields = null, sort = null, limit = 0, skip = 0) => {
  return new Promise((resolve, reject) => {
    Dao.model.find(query)
      .populate(populate || { path: '' })
      .select(fields)
      .sort(sort || { _id: -1 })
      .limit(limit)
      .skip(skip)
      .exec((err, doc) => callback(resolve, reject, err, doc))
  })
}

const counts = (query = null) => {
  return new Promise((resolve, reject) => {
    Dao.count(query, (err, doc) => callback(resolve, reject, err, doc))
  })
}

const updateOne = (query, info) => {
  return new Promise( (resolve, reject) => {
    Dao.updateOne(query, info, (err, doc) => callback(resolve, reject, err, doc))
  })
}

const update = (query, info) => {
  return new Promise( (resolve, reject) => {
    Dao.update(query, info, (err, doc) => callback(resolve, reject, err, doc))
  })
}

const remove = (query) => {
  return new Promise( (resolve, reject) => {
    Dao.delete(query, err => callback(resolve, reject, err))
  })
}

const removeAll = () => {
  return new Promise( (resolve, reject) => {
    Dao.deleteAll( err => callback(resolve, reject, err))
  })
}

const dropAllIndexes = () => {
  return new Promise( (resolve, reject) => {
    Dao.model.collection.dropAllIndexes((err, result) => callback(resolve, reject, err, result))
  })
}

export const clear = () => removeAll().then(dropAllIndexes)

export const createGroup = info => {
  let start = () => new Promise((resolve) => resolve(0))
  if (isMaster(info.level)) {
    start = () => counts({ level: info.level })
  }
  return start()
    .then( ret => {
      if (ret > 0) {
        throw ErrorInfo(CODE.ERROR_GROUP_MASTER_ONLY)
      }
      return addAndUpdateKeys('group')
    })
    .then( ret => create({ ...info, id: ret }))
}

export const getList = (query = null) => {
  let fields =  { id: 1, name: 1, level: 1, team: 1, flag: 1, desc: 1, lock: 1, list: 1 }
  let sort = { level: -1, _id: 1 }
  return find(query, null, fields, sort)
    .then(async ret => {
      for (let e of ret) {
        let counts = await userProxy.counts({ group: e._id })
        e._doc.counts = counts
      }
      return ret
    })
}

export const masterName = async () => {
  let result = await findOne({ level: 9999 })
  return result.name
}

export const editGroup = (query, info) => {
  let result = null
  return findOne(query)
    .then( ret => {
      if (!ret) {
        throw ErrorInfo(CODE.ERROR_DATA_COULD_NOTFIND)
      }
      result = _.pick(ret._doc || ret, ['id', 'name', 'desc', 'level', 'team', 'flag', 'lock', 'list', '_id'])
      let is_update = false
      for (let e of _.keys(info)) {
        if (info[e] !== ret[e]) {
          is_update = true
          break
        }
      }
      if (!is_update) {
        throw ErrorInfo(CODE.ERROR_NODATA_UPDATE_NOCHANGE)
      }
      return updateOne(query, info)
    })
    .then(async ret => {
      let counts = await userProxy.counts({ group: result._id  })
      return { ...result, ...info, counts }
    })
}

export const removeGroup = (_id) => {
  return userProxy.counts({ group: _id })
    .then( ret => {
      if (ret > 0) {
        throw ErrorInfo(CODE.ERROR_REMOVE_GROUP_USER_EXISTS)
      }
      return remove({ _id })
    })
}

export const getObjectId = (query) => {
  return find(query)
    .then( ret => _.map(ret, '_id') )
}

export const getInfo = (query, key = null) => {
  return findOne(query)
    .then( ret => {
      if (ret) {
        return key ? ret[key] : ret
      }
      return key ? undefined : null
    })
}